package util;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class BeanFactory {
    /**
     *  解析xml
     */
    Map<String,Object> map = new HashMap<String,Object>();

    public BeanFactory(String xml){
        parseXml(xml);
    }

    public void parseXml(String xml) throws SpringException {
        //获得路径
        File file = new File(this.getClass().getResource("/").getPath()+"//"+xml);
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(file);
            Element elementRoot = document.getRootElement();

            Attribute attribute = elementRoot.attribute("default-autowire");
            boolean flag=false;
            if (attribute!=null){
                flag=true;
            }
            for (Iterator<Element> itFirlst = elementRoot.elementIterator(); itFirlst.hasNext();) {

                /**
                 * 实例化对象
                 */
                Element elementFirstChil = itFirlst.next();
                Attribute attributeId = elementFirstChil.attribute("id");
                String beanName = attributeId.getValue();
                Attribute attributeClass = elementFirstChil.attribute("class");
                String clazzName  = attributeClass.getValue();
                Class clazz = Class.forName(clazzName);

                /**
                 * 维护依赖关系
                 * 看这个对象有没有依赖（判断是否有property。或者判断类是否有属性）
                 * 如果有则注入
                 */
                Object object = null;
                object=injectBeanBySet(elementFirstChil,object,clazz);

                object=autoInjectBeanByTypeOrName(flag,object,attribute
                        ,clazz);

                if(object==null){//沒有子標簽
                    object = clazz.newInstance();
                }
                map.put(beanName,object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
//        System.out.println(map);
    }

    /**
     * 获得已经注入好的object
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        return map.get(beanName);
    }

    /**
     * 通过set或construct注入对象
     * @param elementFirstChil
     * @param object
     * @param clazz
     * @return
     * @throws Exception
     */
    private Object injectBeanBySet(Element elementFirstChil,Object object, Class clazz) throws Exception{

        for (Iterator<Element> itSecond = elementFirstChil.elementIterator(); itSecond.hasNext();){
            // 得到ref的value，通过value得到对象（map）
            // 得到name的值，然后根据值获取一个Filed的对象
            //通过field的set方法set那个对象
            //<property name="dao" ref="dao"></property>

            Element elementSecondChild = itSecond.next();
            if(elementSecondChild.getName().equals("property")){
                //是否是set，添加依赖关系
                object= clazz.newInstance();
                String refValue = elementSecondChild.attribute("ref").getValue();
                //获取注入对象
                Object injectObject= map.get(refValue) ;
                String nameValue = elementSecondChild.attribute("name").getValue();

                //获取属性 UserDao dao(nameValue) 并为UserDao 附上已经实例化好的值
                Field field = clazz.getDeclaredField(nameValue);
                field.setAccessible(true);
                field.set(object,injectObject);
            }else if(elementSecondChild.getName().equals("constructor-arg")){
                //如果有constructor-arg构造子标签
                String refValue = elementSecondChild.attribute("ref").getValue();
                Object injectObject= map.get(refValue);
                //获取注入对象，并注入
                Class injectObjectClazz = injectObject.getClass();
                Constructor constructor = clazz.getConstructor(injectObjectClazz.getInterfaces()[0]);
                object = constructor.newInstance(injectObject);
            }

        }
        return object;

    }


    /**
     * 模仿byType和byName的自动注入
     * @param flag
     * @param object
     * @param attribute
     * @param clazz
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private Object autoInjectBeanByTypeOrName(Boolean flag,Object object,Attribute attribute,Class clazz
            ) throws IllegalAccessException, InstantiationException{
        if(object==null) {
            if (flag) {
                if (attribute.getValue().equals("byType")) {
                    //判断是否有依赖，得到所有属性
                    Field fields[] = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        //得到属性类型，比如String aa =>field.getType()=String.class
                        Class injectObjectClazz = field.getType();
                        //由于bytype 所以需要遍历map当中的所有对象
                        //判断对象的类型是否和injectObjectClazz相同
                        int count = 0;
                        Object injectObject = null;
                        for (String key : map.keySet()) {
                            Class temp = map.get(key).getClass().getInterfaces()[0];
                            if (temp.getName().equals(injectObjectClazz.getName())) {
                                injectObject = map.get(key);
                                //记录找到一个，因为可能找到多个count
                                count++;
                            }
                        }

                        if (count > 1) {
                            throw new SpringException("需要一个对象，但是找到了两个对象");
                        } else {
                            //实例化并注入
                            object = clazz.newInstance();
                            field.setAccessible(true);
                            field.set(object, injectObject);
                        }
                    }
                }else if(attribute.getValue().equals("byName")){

                    //获取所有属性
                    Field fields[] = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        //得到属性类型名字，比如String aa =>field.getName()=> aa
                        String injectObjectName = field.getName();

                        //由于byName 所以需要遍历标签当中的id
                        //通过id获得injectObject
                        Object injectObject=null;
                        for (String key:map.keySet()){
                            if(key.equals(injectObjectName)){
                                injectObject=map.get(key);
                            }
                        }
                        //注入object里
                        object = clazz.newInstance();
                        field.setAccessible(true);
                        field.set(object, injectObject);
                    }
                }
            }
        }
        return object;
    }
}
